package com.y.fund.service;

import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Service;

import com.y.fund.aacore.able.Idable;
import com.y.fund.aacore.abs.IdObj;
import com.y.fund.aacore.dao.BaseDAO;
import com.y.fund.aacore.dao.HibernateParam;
import com.y.fund.aacore.dao.MatchMode;
import com.y.fund.aacore.data.DataVo;
import com.y.fund.aacore.data.NewDataCodeParam;
import com.y.fund.aacore.data.PaginationList;
import com.y.fund.aacore.enums.BoolVal;
import com.y.fund.aacore.vo.IdVersionVo;
import com.y.fund.aacore.vo.PagingVo;
import com.y.fund.config.ResoConfig;
import com.y.fund.domain.com.CO11DataCode;
import com.y.fund.domain.com.CO12Message;
import com.y.fund.utils.IdGenerator;
import com.y.fund.utils.SQLUtil;
import com.y.fund.utils.StrUtil;

import lombok.Getter;

@Service("commService")
public class CommService{

	@Getter
	@Autowired
	BaseDAO baseDao;

	@Autowired
	ResoConfig resoConfig;

	public Set<Class> getEntitySet(){
		return baseDao.getEntitySet();
	}

    public <T extends Idable> T fillProperties(T nobj) {
    	return fillProperties(nobj, false);
    }

	public <T extends Idable> T fillProperties(T nobj, boolean mustExistInDatabase) {
    	if(null == nobj) {
    		return null;
    	}
    	T svo = null;
    	if(StrUtil.NotBlankIgnoreNullStr(nobj.getId())) {
    		svo = (T) baseDao.findObjById(nobj.getClass(), nobj.getId());
    	}
    	if(null == svo) {
    		if(mustExistInDatabase) {
    			return null;
    		}else {
        		svo = nobj;
        		svo.setVersion(null);
    		}
    	}else {
    		nobj.setVersion(null);
    		BeanUtils.copyProperties(nobj, svo, GetNullPropertyNames(nobj));
    	}

    	return svo;
    }

	/** 为业务数据生成编号  */
	public CO11DataCode newCode(NewDataCodeParam param) {

    	synchronized (null==param.getDataModel()?"code":param.getDataModel()) {
    		/* 防止同一业务多个请求编号时出现并发的情况 */
    		return baseDao.newCode(param);
    	}

	}

	/** 根据实例判断数据的关键属性值【如名称】在数据库中是否存在【不包含本传入对象】  */
	public boolean existData(final Idable nobj) {
		if(null==nobj) {
			return false;
		}
		final String id = nobj.getId();
		List<Idable> list = baseDao.findListByExample(nobj, 2, null, null, new String[]{"id"});
		if(null == list || list.isEmpty()) {
			return false;
		}else if(StrUtil.isBlankIncNullStr(id) || list.size()>1){
			return true;
		}else if(list.get(0).getId().equals(id)) {
			return false;
		}
		/** 数据两条以上，或者存在一条数据但数据的ID与传入的忽略ID值不一致， */
		return true;
	}

	public DataVo addObj(Idable obj) {
		Serializable serializable = this.baseDao.addObj(obj);
		if(null == serializable) {
			return DataVo.FailOperErr();
		}
		return DataVo.Success(serializable);
	}

	/** 添加一条消息，单独定义消息添加的方法，便于拦截器拦截后实时给前端推送提醒  */
	public CO12Message addMsg(CO12Message msg) {
		if(null==msg || StrUtil.isBlankIncNullStr(msg.getOwnerId())) {
			return null;
		}
		if(StrUtil.isBlankIncNullStr(msg.getId())) {
			msg.setId(IdGenerator.Id32());
		}
		if(null == msg.getRead()) {
			msg.setRead(BoolVal.No.getVal());
		}
		if(null == msg.getDone()) {
			msg.setDone(BoolVal.No.getVal());
		}

		if(null == msg.getType()) {
			msg.setType(1);
		}

		if(null != this.baseDao.addObj(msg)) {
			return msg;
		}
		return null;
	}

	public DataVo[] addObjs(Idable... olist) {
		if(null==olist || olist.length<1) {
			return new DataVo[]{DataVo.Fail("No data to save")};
		}
		DataVo [] result = new DataVo[olist.length];
		int i=0;
		for(Idable obj : olist) {
			result[i++] = addObj(obj);
		}
		return result;
	}

	/** 同时保存和修改一条记录 */
	public boolean addAndUpdt(Idable addObj, Idable updtObj) {
		return this.baseDao.addAndUpdt(addObj, updtObj);
	}

	public String addOrUpdtObj(Idable obj) {
		if(this.baseDao.addOrUpdate(obj)) {
			return obj.getId();
		}
		return null;
	}

	/** Update the fields in the object whose data is not null; This object updates the edittime property synchronously */
    public boolean updtByIdIgnoreNulls(Collection<? extends Idable> objs) {
    	if(null==objs || objs.size()<1) {
    		return false;
    	}
    	for(Idable obj : objs) {
        	 baseDao.updtByIdIgnoreNull(obj);
    	}
    	return true;
    }

    public boolean updtByIdIgnoreNull(Idable... objs) {
    	if(null==objs || objs.length<1) {
    		return false;
    	}
    	for(Idable obj : objs) {
        	baseDao.updtByIdIgnoreNull(obj);
    	}
    	return true;
    }

    public boolean updtByIdIgnoreNull(boolean needUpdtVersion, Idable... objs) {
    	if(null==objs || objs.length<1) {
    		return false;
    	}
    	for(Idable obj : objs) {
        	 baseDao.updtByIdIgnoreNull(needUpdtVersion, obj);
    	}
    	return true;
    }

    /** Update map fields in the object whose data is not null  */
    public int updtByIdForKeyVal(boolean needUpdtVersion, Idable obj, Map<String, Object> fieldVals, boolean needOperlog) {
		return baseDao.updtByIdForKeyVal(needUpdtVersion, obj, fieldVals, needOperlog);
    }

	public boolean addOrUpdtObj(Idable... olist) {
		try{
			return baseDao.addOrUpdtObj(olist);
		}catch(Exception e){
			e.printStackTrace();
		}
		return false;
	}

	public boolean addOrUpdtObjs(Collection addUpdtObjs) throws DataAccessException {
		return baseDao.addOrUpdtObjs(addUpdtObjs);
	}

	public boolean addAndUpdtObjs(Collection addObjs, Collection updtObjs) throws DataAccessException {
		return baseDao.addAndUpdtObjs(addObjs, updtObjs);
	}

	public boolean addAndUpdtIgnoreNullObj(Idable addObj, Idable updtObj) {
		return baseDao.addAndUpdtIgnoreNullObj(addObj, updtObj);
	}

	/**保存一个实体对象，根据id修改一个对象 */
	public boolean saveObjAndUpdtIgnoreNullObj(Idable svObj, Idable updtObj) {
		return baseDao.saveObjAndUpdtIgnoreNullObj(svObj, updtObj);
	}

	/**保存一批实体对象，根据id修改一个对象 */
	public boolean saveObjsAndUpdtIgnoreNullObj(Collection svObjs, Idable updtObj) {
		return baseDao.saveObjsAndUpdtIgnoreNullObj(svObjs, updtObj);

	}

	public boolean addAndUpdtIgnoreNullObjs(Collection addObjs, Collection updtObjs) throws DataAccessException {
		return baseDao.addAndUpdtIgnoreNullObjs(addObjs, updtObjs);
	}

	public boolean addOrUpdtObjsAndDels(Collection addUpdtObjs, Collection dels) throws DataAccessException {
		return baseDao.addOrUpdtObjsAndDels(addUpdtObjs, dels);
	}

	public boolean updateObjs(Collection olist) {
		return baseDao.updateObjs(olist);
	}

	public boolean updateObject(Idable... olist) {
		try {
			baseDao.updateObject(olist);
			return true;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return false;
	}
	

	/**使用SQL语句查询记录 */
	public List queryBySQL(final String sql, Collection<HibernateParam> params){
		return baseDao.queryBySQL(sql, params);
	}
	
	/**执行SQL语句,并返回受影响的记录条数 */
	public int execSQL(final String sql, Collection<HibernateParam> params){
		return baseDao.execSQL(sql, params);
	}

	public int execUpdtHql(final String hql,Collection<HibernateParam> params){
		return baseDao.execUpdtHql(hql, params);
	}

	/** Physically delete a piece of data according to the ID, and the data cannot be recovered after deletion; */
	public DataVo delObjById(Class clazz, Serializable id) {
		Object entity = baseDao.findObjById(clazz, id);
		if(null == entity) {
			return DataVo.FailNotData();
		}else {
			baseDao.deleteEntity(entity);
			return DataVo.Success(id);
		}
	}

	/** Delete a single persistent object  */
	public boolean deleteEntity(Object ... olist) {
		try{
			if(null==olist || olist.length<1) {
				return false;
			}
			for(Object obj : olist) {
				baseDao.deleteEntity(obj);
			}
			return true;
		}catch(Exception e){
			e.printStackTrace();
		}
		return false;
	}

	/** Batch delete persistent objects */
	public boolean deleteEntitys(Collection delObjs) throws DataAccessException {
		if(null==delObjs || delObjs.size()<1) {
			return false;
		}
		baseDao.deleteEntitys(delObjs);
		return true;
	}

	/** Save and delete a single persistent object  */
	public void saveAndDel(Idable saveObj, Object entity) throws DataAccessException {
		baseDao.addOrUpdate(saveObj);
		baseDao.deleteEntity(entity);
	}

	public boolean addObjs(Collection<? extends Idable> objs) throws DataAccessException{
		if(null==objs || objs.size()<1) {
			return true;
		}
		for(Idable obj : objs) {
			addObj(obj);
		}
		return true;
	}


	public void evictObj(Object obj) throws DataAccessException {
		if(null != obj){
			baseDao.evictObj(obj);
		}
	}

	/**
	 * @param hql
	 * @return
	 */
	public List findObjects(String hql, Integer maxSize) {
		return baseDao.findObjects(hql, null, maxSize);
	}

	/**
	 * @param hql
	 * @param params
	 * @param maxSize
	 * @return
	 */
	public List findObjects(String hql, List<HibernateParam> params, Integer maxSize) {
		return baseDao.findObjects(hql, params, maxSize);
	}

	public <T> T findObjById(Class<T> clazz, Serializable id) {
		return this.baseDao.findObjById(clazz, id);
	}

	/** Find a single object with the same attribute value according to the example  */
	public <T extends Idable> T findObjByExample(T instance, String [] fieldNames) {
		return this.baseDao.findObjByExample(instance, fieldNames);
	}

	/** Find a single object with the same attribute value according to the example  */
	public <T extends Idable> T findObjByExample(final T instance, final LinkedHashMap<String,String> orderMap, String  [] fieldNames) {
		return this.baseDao.findObjByExample(instance, orderMap,fieldNames);
	}

	/**  Find the list of objects with the same attribute value according to the example */
	public <T extends Idable>List<T> findListByExample(final T instance){
		return findListByExample(instance, null, null);
	}

	public <T extends Idable> List<T> findListFieldsByExample(final T instance, String[] fieldNames) {
		return this.baseDao.findListFieldsByExample(instance, fieldNames);
	}

	public <T extends Idable>List<T> findListFieldsByExp(final T instance, final LinkedHashMap<String,String> orderMap, String [] fieldNames){
		return this.baseDao.findListByExpMatchMode(MatchMode.EXACT, instance, null, orderMap, fieldNames);
	}

	/**
	 * 根据ID及示例对象的非空字段精确匹配查找数据记录
	 */
	public <T extends Idable> List<T> findListByExpAndIdIn(final T instance, Collection idVals, final LinkedHashMap<String, String> orderMap) {
		return this.baseDao.findListByExpAndIdIn(instance, idVals, orderMap, null);
	}
	/**
	 * 根据ID集合查找实例列表
	 */
	public <T extends Idable> List<T> findListByExpAndIdIn(final T instance, Collection idVals, final LinkedHashMap<String, String> orderMap, String[] fieldNames) {
		return this.baseDao.findListByExpAndIdIn(instance, idVals, orderMap, fieldNames);
	}

	/**
	 * 根据示例对象的相同属性字段值查询数据列表
	 */
	public <T extends Idable> List<T> findListByExpAndIn(final T instance, String fieldName, Collection values) {
		return this.baseDao.findListByExpAndFieldIn(instance, fieldName, values, null, null);
	}

	/**  Find the list of objects with the same attribute value according to the example */
	public <T extends Idable>List<T> findListByExpAndIn(final T instance, String fieldName, Object[] values){
		if(StrUtil.NotBlankIgnoreNullStr(fieldName) && null!=values && values.length>0) {
			return this.baseDao.findListByExpAndFieldIn(instance, fieldName, Arrays.asList(values), null, null);
		}else {
			return findListByExample(instance, null, null);
		}
	}
	
	
	/**  根据示例对象查找属性值相同的数据列表 */
	public <T extends Idable>List<T> findListByExample(final T instance, final LinkedHashMap<String,String> orderMap){
		return findListByExample(instance, null, orderMap);
	}
	/**  根据示例对象查找属性值相同的数据列表，可指定最大数据集 */
	public <T extends Idable>List<T> findListByExample(final T instance, Integer maxSize, final LinkedHashMap<String,String> orderMap){
		return this.baseDao.findListByExpOrder(instance, maxSize, orderMap);
	}

	/**  根据示例查找属性值相同的对象列表 */
	public <T>List<T> findListByExp2Bean(final IdObj instance, Class<T> toBeanClass,  String [] fieldNames){
		return this.baseDao.findListByExample(instance, SQLUtil.MAX_QUERY_SIZE, null, toBeanClass, fieldNames);
	}


	/**
	 * 根据示例对象查找属性值相似的数据列表，可指定最大数据集
	 */
	public <T extends Idable> List<T> findListByExpLike(final T instance, Integer maxSize, final LinkedHashMap<String, String> orderMap) {
		return this.baseDao.findListByExpLike(instance, maxSize, orderMap);
	}

	public <T extends Idable> List<T> findListFieldsByExpLike(final T instance, String[] fieldNames) {
		return this.baseDao.findListFieldsByExpLike(instance, fieldNames);
	}

	/** Find the list of paging objects with the same attribute value according to the example */
	public <T> PaginationList<T> findPagingByExpLikeCountById(final Idable instance, PagingVo pvo, String [] fieldNames){
		return this.baseDao.findPagingByExpLike(instance, pvo, null, fieldNames, null);
	}
	

	/**  根据样例数据查询相同属性值的列表数据，根据ID统计数量 */
	public <T> PaginationList<T> findPagingByExpLikeOrderCountById(final Idable instance, PagingVo pvo, final LinkedHashMap<String,String> orderMap){
		return this.baseDao.findPagingByExpLike(instance, pvo, orderMap, null, null);
	}


	/** Find the list of paging objects with the same attribute value according to the example， count size  by id. */
	public <T> PaginationList<T> findPagingByExpLikeCountById(final Idable instance, PagingVo pvo, final LinkedHashMap<String,String> orderMap, String [] fieldNames) {
		return this.baseDao.findPagingByExpLike(instance, pvo, orderMap, fieldNames, null);
	}

	/** Find the list of paging objects with the same attribute value according to the example， count size  by id. */
	public <T> PaginationList<T> findPagingByExpLikeCountById(final Idable instance, PagingVo pvo, final LinkedHashMap<String,String> orderMap, String [] fieldNames, Class toBeanClass) {
		return this.baseDao.findPagingByExpLike(instance, pvo, orderMap, fieldNames, toBeanClass);
	}

	/** Find the list of paging objects with the same attribute value according to the example， count size  by id. */
	public <T extends Idable> PaginationList<T> findPagingByExpCountById(final T instance, PagingVo pvo, final LinkedHashMap<String,String> orderMap){
		return this.baseDao.findPagingByExample(instance, pvo, orderMap, null);
	}


	/** Find the list of paging objects with the same attribute value according to the example， count size  by id. */
	public <T extends Idable> PaginationList<T> findPagingByExpCountById(final T instance, PagingVo pvo, final LinkedHashMap<String,String> orderMap, String [] fieldNames) {
		return this.baseDao.findPagingByExample(instance, pvo, orderMap, fieldNames);
	}

	/**  Press the "Id" field to find the number of objects with the same set attribute value according to the example  */
	public int findSizeByExpCountById(Idable instance) {
		return this.baseDao.findSizeByExpCountById(instance);
	}

	/**
	 * Clean up data in Hibernate sessions.
	 */
	public void clearSession() {
		baseDao.clearSession();
	}

	public PaginationList findPaginatedList(String hql, List<HibernateParam> params, PagingVo pvo) {
		return baseDao.findPaginatedList(hql, params, pvo, false);
	}

	public long findSizeByHql(final String hql, List<HibernateParam> params) throws DataAccessException {
		return baseDao.findSizeByHql(hql, params);
	}

	public <R> List<R> execuPageSql(String hql, Integer pageSize, Integer pageNum, Map<String, Object> params, Class<R> clazz) {
		return baseDao.execuQuery(hql, pageSize, pageNum, params, clazz);
	}

	public long countBySql(String sql, Map<String, Object> params) {
		return baseDao.countBySql(sql, params);
	}

	/**
	 * 根据ID查询此数据的数据版本号
	 */
	public Integer findVersionById(Class clazz, String id) {
		return baseDao.findVersionById(clazz, id);
	}

	public IdVersionVo findIdVersionVoById(Class clazz, String id) {
		return baseDao.findIdVersionVoById(clazz, id);
	}

    public static String[] GetNullPropertyNames (Object source) {
    	if(null == source) {
    		return null;
    	}
        final BeanWrapper src = new BeanWrapperImpl(source);
        java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors();

        Set<String> emptyNames = new HashSet<String>();
        for(java.beans.PropertyDescriptor pd : pds) {
            Object srcValue = src.getPropertyValue(pd.getName());
            if (srcValue == null) emptyNames.add(pd.getName());
		}
		String[] result = new String[emptyNames.size()];
		return emptyNames.toArray(result);
	}

	public Object findUniqueObject(final String hql, final List<HibernateParam> params) {

		return baseDao.findUniqueObject(hql, params);
	}

	public int delObjByIds(Class clazz, Collection<String> delIds) {
		return baseDao.delObjByIds(clazz, delIds);
	}

	public List findListBySQL(String sql, Map<String, Object> params) {
		return baseDao.findListBySQL(sql, params);
	}

}
